/**
 * Copyright (C), -2025, www.bosssof.com.cn
 * @FileName RoleServiceImpl.java
 * @Author Administrator
 * @Date 2022-9-30  16:25
 * @Description 包含角色管理界面的功能实现
 * History:
 * <author> Administrator
 * <time> 2022-9-30  16:25
 * <version> 1.0.0
 * <desc> abel.zhan 优化代码结构 分配用户方法采用清除用户角色重新分配，优化角色的多资源分配方式 优化代码结构和效率改为批量插入
 */
package com.sd365.permission.centre.service.impl;

import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import com.github.pagehelper.page.PageMethod;
import com.sd365.common.core.annotation.stuffer.CommonFieldStuffer;
import com.sd365.common.core.annotation.stuffer.IdGenerator;
import com.sd365.common.core.annotation.stuffer.MethodTypeEnum;
import com.sd365.common.core.common.aop.MyPageInfo;
import com.sd365.common.core.common.constant.EntityConsts;
import com.sd365.common.core.common.exception.BusinessException;
import com.sd365.common.core.common.exception.DaoException;
import com.sd365.common.core.common.exception.code.BusinessErrorCode;
import com.sd365.common.core.common.pojo.entity.TenantBaseEntity;
import com.sd365.common.core.context.BaseContextHolder;
import com.sd365.common.util.BeanException;
import com.sd365.common.util.BeanUtil;
import com.sd365.permission.centre.dao.mapper.*;
import com.sd365.permission.centre.entity.*;
import com.sd365.permission.centre.pojo.dto.*;
import com.sd365.permission.centre.pojo.query.RoleQuery;
import com.sd365.permission.centre.pojo.query.UserQuery;
import com.sd365.permission.centre.pojo.vo.RoleCompanyVO;
import com.sd365.permission.centre.service.RoleService;
import com.sd365.permission.centre.service.exception.UserCentreExceptionCode;
import com.sd365.permission.centre.service.impl.AbstractBaseDataServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import tk.mybatis.mapper.entity.Example;

import javax.validation.Valid;
import javax.validation.constraints.AssertTrue;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Class RoleServiceImpl
 * @Description able.zhan 2022-09-30 重构了 人员分配
 * @Author Administrator
 * @Date 2022-9-30  16:24
 * @version 1.0.0
 */
@Service
@Slf4j
public class RoleServiceImpl extends AbstractBaseDataServiceImpl implements RoleService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private RoleResourceMapper roleResourceMapper;

    @Autowired
    private RoleCompanyMapper roleCompanyMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private IdGenerator idGenerator;

    @Autowired
    private ResourceMapper resourceMapper;

    @Autowired
    private PositionMapper positionMapper;

    @Autowired
    private CompanyMapper companyMapper;

    @Autowired
    private DepartmentMapper departmentMapper;

    @Override
    public Boolean initRoleResourceRedis(Role role) {
        List<Role> roles = roleMapper.commonQuery(role);
        for (Role role1 : roles) {
            List<Node> nodeList = roleResourceMapper.queryResourceByRoleId(role1.getId());
            List<Resource> resources = new ArrayList<>();
            try {
                for (Node node : nodeList) {
                    resources.add(resourceMapper.findById(node.getId()));
                }
                if (redisTemplate.opsForValue().get(role1.getId().toString()) == null) {
                    String json = JSON.toJSONString(resources);
                    redisTemplate.opsForValue().set(role1.getId().toString(), json);
                }
            } catch (Exception e) {
                redisTemplate.delete(role1.getId().toString());
                e.printStackTrace();
            }
        }
        return true;
    }

    @Override
    public Boolean assignResource(RoleResourceDTO roleResourceDTO) {
        // 给多个角色分配多个资源
        List<Long> roleIds = roleResourceDTO.getRoleIds();
        List<Long> resourceIds = roleResourceDTO.getResourceIds();
        RoleResource roleResource = new RoleResource();
        for (Long roleId : roleIds) {
            roleResource.setRoleId(roleId);
            List<Resource> resources = new ArrayList<>();

            // 先清空对应角色的所有资源信息，然后重新插入
            roleResourceMapper.deleteByRoleId(roleId);

            try {
                for (Long resourceId : resourceIds) {
                    Object o = redisTemplate.opsForValue().get(resourceId.toString());
                    Resource resource = JSONObject.parseObject(JSON.toJSONString(o), Resource.class);
                    resources.add(resource);

                    // 单条数据(角色-资源信息)装填
                    super.baseDataStuff4Add(roleResource);
                    super.baseDataStuffCommonPros(roleResource);
                    roleResource.setId(idGenerator.snowflakeId());
                    roleResource.setResourceId(resourceId);
                    roleResource.setStatus((byte) 0);
                    roleResource.setVersion(1L);

                    int result = roleResourceMapper.insert(roleResource);
                    if (result < 0) {
                        throw new DaoException(UserCentreExceptionCode.RESOURCE_ASSIGN_FAILED, null);
                    }

                }
            } catch (BusinessException e) {
                throw new BusinessException(UserCentreExceptionCode.RESOURCE_ASSIGN_FAILED, e);
            }
            String json = JSON.toJSONString(resources);
            redisTemplate.opsForValue().set(roleId.toString(), json);
        }
        return true;

    }

    @Override
    public Boolean assignUser(UserRoleDTO userRoleDTO) {
        Assert.isTrue(userRoleDTO!=null && userRoleDTO.getRoleIds()!=null && userRoleDTO.getUserIds()!=null,
                "角色和用户必须不能为空");
        //给多个角色分配多个资源
        List<Long> roleIds = userRoleDTO.getRoleIds();
        List<Long> userIds = userRoleDTO.getUserIds();

        // 删除原来的角色所对用的用户
        Example example=new Example(UserRole.class);
        example.createCriteria().andIn("roleId",roleIds);
        int deletedRows=userRoleMapper.deleteByExample(example);

        // 插入角色所包含的新用户
        UserRole userRole = new UserRole();
        int insertRows=0;
        for (Long roleId : roleIds) {
            userRole.setRoleId(roleId);
            // 逐个插入用户，用户不多此操作无大影响
            for (Long userId : userIds) {
                userRole.setId(idGenerator.snowflakeId());
                userRole.setUserId(userId);
                userRole.setStatus((byte) INITIALIZE_STATUS_VALUE);
                userRole.setVersion(INITIALIZE_VERSION_VALUE);
                // 公用字段补充未来直接myabtis插件填写
                super.baseDataStuff4Add(userRole);
                super.baseDataStuffCommonPros(userRole);
                insertRows = userRoleMapper.insert(userRole);
            }
        }
        return insertRows>0;
    }

    @Override
    public List<Node> queryResourceByRoleId(long roleId) {
        try {
            return roleResourceMapper.queryResourceByRoleId(roleId);
        } catch (Exception ex) {
            throw new BusinessException(UserCentreExceptionCode.BUSINESS_USER_CENTRE_ROLE_DB_EXCEPTION_CODE, ex);
        }
    }

    @Override
    public List<Node> selectCompany() {
        return roleMapper.selectCompany();
    }

    @Override
    public boolean haveRole(@Valid RoleDTO roleDTO) {
        Role role = BeanUtil.copy(roleDTO, Role.class);
        return roleMapper.haveRole(role) > 0;
    }

    @Override
    @CommonFieldStuffer(methodType = MethodTypeEnum.ADD)
    public Boolean add(@Valid RoleDTO roleDTO) {

//        super.baseDataStuff4Add(roleDTO);
//        super.baseDataStuffCommonPros(roleDTO);

        Role role = BeanUtil.copy(roleDTO, Role.class);
        role.setId(idGenerator.snowflakeId());

        role.setOrgId(1337955940401545216L);
        role.setCompanyId(1337960193258029056L);
        return roleMapper.insert(role) > 0;
    }


    @Override
    @CommonFieldStuffer(methodType = MethodTypeEnum.DELETE)
    public Boolean remove(Long id, Long version) {
        Example exampleRole = new Example(Role.class);
        exampleRole.createCriteria().andEqualTo("id", id).andEqualTo("version", version);

        Example exampleRoleResource = new Example(RoleResource.class);
        exampleRoleResource.createCriteria().andEqualTo("roleId", id);

        Example exampleUserRole = new Example(UserRole.class);
        exampleUserRole.createCriteria().andEqualTo("roleId", id);

        roleResourceMapper.deleteByExample(exampleRoleResource);
        userRoleMapper.deleteByExample(exampleUserRole);
        return roleMapper.deleteByExample(exampleRole) > 0;
    }


    @Override
    @CommonFieldStuffer(methodType = MethodTypeEnum.UPDATE)
    public RoleDTO modify(RoleDTO roleDTO) {
        try {
            Role role = BeanUtil.copy(roleDTO, Role.class);
            role.setUpdatedTime(new Date());
            Example example = new Example(Role.class);
            example.createCriteria().andEqualTo("id", roleDTO.getId()).andEqualTo("version", roleDTO.getVersion());

            super.baseDataStuff4Updated(role);
            if (roleMapper.updateByExample(role, example) > 0) {
                RoleDTO roleDTOCache = BeanUtil.copy(role, RoleDTO.class);
                BeanUtil.copy(role.getCompany(), roleDTOCache.getCompanyDTO(), CompanyDTO.class);
                BeanUtil.copy(role.getOrganization(), roleDTOCache.getOrganizationDTO(), OrganizationDTO.class);
                BeanUtil.copy(role.getTenant(), roleDTOCache.getTenantDTO(), TenantDTO.class);
                return roleDTOCache;
            }
            return null;
        } catch (BeanException ex) {
            throw new BusinessException(BusinessErrorCode.SYSTEM_BEAN_COPY_EXCEPTION, ex);
        } catch (Exception ex) {
            throw new BusinessException(UserCentreExceptionCode.BUSINESS_USER_CENTRE_ROLE_DB_EXCEPTION_CODE, ex);

        }
    }

    @Override
    public List<RoleDTO> commonQuery(RoleQuery roleQuery) {
        Role role = BeanUtil.copy(roleQuery,Role.class);
        /*
        设置分页
         */
        PageMethod.startPage(roleQuery.getPageNum(),roleQuery.getPageSize());
        List<Role> roles = roleMapper.commonQuery(role);
        PageInfo<Role> pageInfo = new PageInfo<>(roles);
        /*
        将信息拷贝到ThreadLocal
         */
        BaseContextHolder.set("pageInfo",JSON.toJSONString(new MyPageInfo(pageInfo.getTotal(),pageInfo.getPages())));
        /*
        TODO 可能在copy的时候会报错 要进行异常处理
         */
        return BeanUtil.copyList(roles,RoleDTO.class);
    }

    @Override
    @CommonFieldStuffer(methodType = MethodTypeEnum.QUERY)
    public RoleDTO queryById(Long id) {
        try {
            RoleDTO roleDTO = null;
            Role role = roleMapper.selectById(id);
            if (role != null) {
                roleDTO = BeanUtil.copy(role, RoleDTO.class);
                BeanUtil.copy(role.getCompany(), roleDTO.getCompanyDTO(), CompanyDTO.class);
                BeanUtil.copy(role.getOrganization(), roleDTO.getOrganizationDTO(), OrganizationDTO.class);
                BeanUtil.copy(role.getTenant(), roleDTO.getTenantDTO(), TenantDTO.class);
            }
            return roleDTO;
        } catch (BeanException ex) {
            throw new BusinessException(BusinessErrorCode.SYSTEM_BEAN_COPY_EXCEPTION, ex);
        } catch (Exception ex) {
            throw new BusinessException(UserCentreExceptionCode.BUSINESS_USER_CENTRE_ROLE_DB_EXCEPTION_CODE, ex);
        }
    }

    @Override
    public RoleDTO copy(Long id) {
        if (null == id) {
            throw new RuntimeException("传输参数为空异常:copy方法");
        }
        Role role = roleMapper.selectByPrimaryKey(id);
        if (role != null) {
            RoleDTO roleDTO = BeanUtil.copy(role, RoleDTO.class);
            return roleDTO;
        } else {
            return new RoleDTO();
        }


        /**
         查询 id的角色信息
         修改 id 插入行的角色信息
         */
    }

    @Override
    @CommonFieldStuffer(methodType = MethodTypeEnum.DELETE)
    public Boolean batchRemove(RoleDTO[] roleDTOS) {
        Assert.notEmpty(roleDTOS, "批量删除参数不可以为空");
        for (RoleDTO roleDTO : roleDTOS) {
            Example exampleRoleResource = new Example(RoleResource.class);
            exampleRoleResource.createCriteria().andEqualTo("roleId", roleDTO.getId());
            roleResourceMapper.deleteByExample(exampleRoleResource);

            Example exampleUserRole = new Example(UserRole.class);
            exampleUserRole.createCriteria().andEqualTo("roleId", roleDTO.getId());
            userRoleMapper.deleteByExample(exampleUserRole);
        }
        for (RoleDTO roleDTO : roleDTOS) {
            Example example = new Example(Role.class);
            example.createCriteria().andEqualTo("id", roleDTO.getId()).andEqualTo("version", roleDTO.getVersion());
            int result = roleMapper.deleteByExample(example);
            Assert.isTrue(result > 0, String.format("删除的记录id %d 没有匹配到版本", roleDTO.getId()));
        }
        return true;
    }

    @Override
    public List<UserDTO> commonQueryUser(UserQuery userQuery) {
        try {

            Example example = new Example(User.class);
            example.setOrderByClause("updated_time DESC");

            Example.Criteria criteria = example.createCriteria();

            if (userQuery.getCode() != null) {//模糊查询
                criteria.orLike("code", "%" + userQuery.getCode() + "%");
            } else {
                criteria.orLike("code", userQuery.getCode());
            }
            if (userQuery.getName() != null) {//模糊查询
                criteria.orLike("name", "%" + userQuery.getName() + "%");
            } else {
                criteria.orLike("name", userQuery.getName());
            }

            List<User> users = userMapper.selectByExample(example);
            Page page = (Page) users;
            BaseContextHolder.set("pageInfo", JSON.toJSONString(new MyPageInfo(page.getTotal(), page.getPages())));

            List<UserDTO> userDTOS = BeanUtil.copyList(users, UserDTO.class);
            if (!CollectionUtils.isEmpty(userDTOS)) {
                List<Company> companies = companyMapper.selectAll();
                Map<Long, CompanyDTO> companyVOMap = new HashMap<>();
                for (Company company : companies) {
                    companyVOMap.put(company.getId(), BeanUtil.copy(company, CompanyDTO.class));
                }
                List<Position> positions = positionMapper.selectAll();
                Map<Long, PositionDTO> positionHashMap = new HashMap<>();
                for (Position position : positions) {
                    positionHashMap.put(position.getId(), BeanUtil.copy(position, PositionDTO.class));
                }


                List<Department> departments = departmentMapper.selectAll();
                Map<Long, DepartmentDTO> departmentDTOMap = new HashMap<>();
                for (Department department : departments) {
                    departmentDTOMap.put(department.getId(), BeanUtil.copy(department, DepartmentDTO.class));
                }


                for (UserDTO userDTO : userDTOS) {
                    if (!ObjectUtils.isEmpty(userDTO.getCompanyId())) {
                        userDTO.setCompany(companyVOMap.get(userDTO.getCompanyId()));
                    }
                    if (!ObjectUtils.isEmpty(userDTO.getPositionId())) {
                        userDTO.setPosition(positionHashMap.get(userDTO.getPositionId()));
                    }
                    if (!ObjectUtils.isEmpty(userDTO.getDepartmentId())) {
                        userDTO.setDepartment(departmentDTOMap.get(userDTO.getDepartmentId()));
                    }
                }
            }

            return userDTOS;
        } catch (BeanException ex) {
            throw new BusinessException(BusinessErrorCode.SYSTEM_BEAN_COPY_EXCEPTION, ex);
        } catch (Exception ex) {
            throw new BusinessException(UserCentreExceptionCode.BUSINESS_USER_CENTRE_ROLE_DB_EXCEPTION_CODE, ex);

        }
    }

    @Override
    public RoleDTO queryUserResource(Long id) {
        Role role = roleMapper.selectByPrimaryKey(id);
        Example example = new Example(RoleResource.class);
        example.selectProperties("resourceId");
        example.createCriteria().andEqualTo("roleId", id);
        List<RoleResource> roleResources = roleResourceMapper.selectByExample(example);
        Set<Long> idSet = new HashSet<>();
        for (RoleResource roleResource : roleResources) {
            idSet.add(roleResource.getResourceId());
        }
        if (!idSet.isEmpty()) {
            Example re = new Example(Resource.class);
            re.createCriteria().andIn("id", idSet);
            List<Resource> resources = resourceMapper.selectByExample(re);
            List<ResourceDTO> resourceDTOS = BeanUtil.copyList(resources, ResourceDTO.class);

            RoleDTO copy = BeanUtil.copy(role, RoleDTO.class);
            copy.setResourceDTOS(resourceDTOS);
            return copy;
        } else {
            return null;
        }
    }

    @Override
    public RoleCompanyVO queryRoleCompanyById(Long id) {
        Example example = new Example(RoleCompany.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("roleId", id);
        // 获取对应角色id的 角色-授权公司 列表
        List<RoleCompany> roleCompanyList = roleCompanyMapper.selectByExample(example);
        List<Long> authCompanyIds = roleCompanyList.stream().map(RoleCompany::getAuthCompanyId)
                .collect(Collectors.toList());
        RoleCompanyVO roleCompanyVO = new RoleCompanyVO();
        roleCompanyVO.setRoleId(id);
        roleCompanyVO.setAuthCompanyIds(authCompanyIds);
        return roleCompanyVO;
    }

    @Override
    public Boolean modifyRoleCompany(RoleCompanyDTO roleCompanyDTO) {

        List<Long> roleIds = roleCompanyDTO.getRoleIds();
        for (Long roleId : roleIds) {
            Example selectExample = new Example(RoleCompany.class);
            Example.Criteria selectCriteria = selectExample.createCriteria();
            selectCriteria.andEqualTo("roleId", roleId);
            // 获取数据库 原有的(old) 对应角色id的 角色-授权公司 id列表
            List<RoleCompany> roleCompanyList = roleCompanyMapper.selectByExample(selectExample);
            List<Long> oldAuthCompanyIdList = roleCompanyList.stream()
                    .map(RoleCompany::getAuthCompanyId).collect(Collectors.toList());
            // 获取前端传来的 最新的(new) 角色id的 角色-授权公司 id列表
            List<Long> newAuthCompanyIdList = roleCompanyDTO.getAuthCompanyIds();

            // 旧的id列表去掉已经在新的id列表的那部分（去掉交集）是准备要删除的
            List<Long> toDelete = oldAuthCompanyIdList.stream()
                    .filter(id -> !newAuthCompanyIdList.contains(id)).collect(Collectors.toList());
            // 新的id列表去掉在旧的id列表的那部分（去掉交集）是准备要添加的
            List<Long> toInsert = newAuthCompanyIdList.stream()
                    .filter(id -> !oldAuthCompanyIdList.contains(id)).collect(Collectors.toList());

            if (!toDelete.isEmpty()) {
                Example deleteExample = new Example(RoleCompany.class);
                Example.Criteria deleteCriteria = deleteExample.createCriteria();
                deleteCriteria.andEqualTo("roleId", roleId).andIn("authCompanyId", toDelete);
                roleCompanyMapper.deleteByExample(deleteExample);
            }

            List<RoleCompany> insertRoleCompanyList = new ArrayList<>();
            toInsert.forEach(authCompanyId -> {
                RoleCompany roleCompany = new RoleCompany();
                roleCompany.setRoleId(roleId);
                roleCompany.setAuthCompanyId(authCompanyId);
                initEntity(roleCompany);
                insertRoleCompanyList.add(roleCompany);
            });
            if (!insertRoleCompanyList.isEmpty()) {
                roleCompanyMapper.insertList(insertRoleCompanyList);
            }
        }

        return true;
    }

    public void initEntity(TenantBaseEntity entity) {
        entity.setId(idGenerator.snowflakeId());
        baseDataStuff4Add(entity);
        baseDataStuffCommonPros(entity);
        entity.setStatus(EntityConsts.INITIAL_STATUS);
        entity.setVersion(EntityConsts.INITIAL_VERSION);
    }
}
